<?php

require_once dirname(__FILE__) . '/DataQuery.php';

/**
 * 数据供给器接口
 *
 * - 接口抽离，便于在高层统一接口
 * - 通过多级缓存来优化性能
 * - 支持全球追踪器链路收集
 *
 * @author dogstar 2018-06-17
 */

abstract class DataProvider {
    /**
     * 获取业务数据
     *
     * @param DataQuery $query 获取业务数据的查询对象
     * @param string &$trace 全球追踪收集器
     * @return mixed/NULL 业务数据，NULL表示获取失败
     */
    public function getData(DataQuery $query, &$trace = '') {
        $cache = $this->getCacheInstance();
        $key = $this->getCacheKey($query);

        // 为缓存添加后缀，支持每10分钟，切换一份缓存
        $key .= '_' . intval(date('i', $query->nowTime) / 10 % 2);

        // 从缓存中获取（追加读开关控制）
        $data = $query->cacheRead ? $cache->get($key) : NULL;
        if ($data === NULL) {
            // 从数据源获取
            $data = $this->doGetData($query, $trace);

            // 写入到缓存（追加写开关控制）
            if ($query->cacheWrite) {
                $cache->set($key, $data, $this->getCacheExpireTime($query));
            }
        }

        // 钩子函数回调
        $this->afterGetData($query, $data);

        return $data;
    }

    /**
     * 返回需要缓存的原始数据
     *
     * @param DataQuery $query 获取业务数据的查询对象
     * @param string &$trace 全球追踪收集器
     * @return mixed 注意：没有数据时请返回空数组array()，或者''，切勿返回NULL/FALSE
     */
    abstract protected function doGetData(DataQuery $query, &$trace = '');

    /**
     * 钩子函数：数据的实时处理操作，将会在业务数据成功获取后进行回调
     * @param  mixed &$data 业务数据
     * @return mixed 加工处理后的业务数据
     */
    protected function afterGetData(DataQuery $query, &$data) {
        // 可选实现：加工处理
    }

    /**
     * 获取缓存实例
     * @return Cache 缓存实例
     */
    abstract protected function getCacheInstance();

    /**
     * 获取缓存的Key，返回唯一缓存key，这里将$query传入，以便同类数据根据不同的值生成不同的key
     *
     * @param DataQuery $query 获取业务数据的查询对象
     * @return string 缓存key
     */
    abstract protected function getCacheKey(DataQuery $query);

    /**
     * 返回缓存有效时间，单位为：秒
     *
     * @param DataQuery $query 获取业务数据的查询对象
     * @return int 缓存有效时间
     */
    abstract protected function getCacheExpireTime(DataQuery $query);
}
